home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / MONITOR.PY < prev    next >
Encoding:
Python Source  |  2000-08-03  |  8.9 KB  |  350 lines

  1. # -*- Mode: Python; tab-width: 4 -*-
  2. #    Author: Sam Rushing <rushing@nightmare.com>
  3.  
  4. #
  5. # python REPL channel.
  6. #
  7.  
  8. RCS_ID = '$Id: monitor.py,v 1.8.4.1 2000/08/03 19:01:35 brian Exp $'
  9.  
  10. import md5
  11. import socket
  12. import string
  13. import sys
  14. import time
  15. import traceback
  16.  
  17. VERSION = string.split(RCS_ID)[2]
  18.  
  19. import asyncore
  20. import asynchat
  21.  
  22. from counter import counter
  23. import producers
  24.  
  25. class monitor_channel (asynchat.async_chat):
  26.     try_linemode = 1
  27.  
  28.     def __init__ (self, server, sock, addr):
  29.         asynchat.async_chat.__init__ (self, sock)
  30.         self.server = server
  31.         self.addr = addr
  32.         self.set_terminator ('\r\n')
  33.         self.data = ''
  34.         # local bindings specific to this channel
  35.         self.local_env = sys.modules['__main__'].__dict__.copy()
  36.         self.push ('Python ' + sys.version + '\r\n')
  37.         self.push (sys.copyright+'\r\n')
  38.         self.push ('Welcome to %s\r\n' % self)
  39.         self.push ("[Hint: try 'from __main__ import *']\r\n")
  40.         self.prompt()
  41.         self.number = server.total_sessions.as_long()
  42.         self.line_counter = counter()
  43.         self.multi_line = []
  44.         
  45.     def handle_connect (self):
  46.         # send IAC DO LINEMODE
  47.         self.push ('\377\375\"')
  48.  
  49.     def close (self):
  50.         self.server.closed_sessions.increment()
  51.         asynchat.async_chat.close(self)
  52.  
  53.     def prompt (self):
  54.         self.push ('>>> ')
  55.  
  56.     def collect_incoming_data (self, data):
  57.         self.data = self.data + data
  58.         if len(self.data) > 1024:
  59.             # denial of service.
  60.             self.push ('BCNU\r\n')
  61.             self.close_when_done()
  62.  
  63.     def found_terminator (self):
  64.         line = self.clean_line (self.data)
  65.         self.data = ''
  66.         self.line_counter.increment()
  67.         # check for special case inputs...
  68.         if not line and not self.multi_line:
  69.             self.prompt()
  70.             return
  71.         if line in ['\004', 'exit']:
  72.             self.push ('BCNU\r\n')
  73.             self.close_when_done()
  74.             return
  75.         oldout = sys.stdout
  76.         olderr = sys.stderr
  77.         try:
  78.             p = output_producer(self, olderr)
  79.             sys.stdout = p
  80.             sys.stderr = p
  81.             try:
  82.                 # this is, of course, a blocking operation.
  83.                 # if you wanted to thread this, you would have
  84.                 # to synchronize, etc... and treat the output
  85.                 # like a pipe.  Not Fun.
  86.                 #
  87.                 # try eval first.  If that fails, try exec.  If that fails,
  88.                 # hurl.
  89.                 try:
  90.                     if self.multi_line:
  91.                         # oh, this is horrible...
  92.                         raise SyntaxError
  93.                     co = compile (line, repr(self), 'eval')
  94.                     result = eval (co, self.local_env)
  95.                     method = 'eval'
  96.                     if result is not None:
  97.                         print repr(result)
  98.                     self.local_env['_'] = result
  99.                 except SyntaxError:
  100.                     try:
  101.                         if self.multi_line:
  102.                             if line and line[0] in [' ','\t']:
  103.                                 self.multi_line.append (line)
  104.                                 self.push ('... ')
  105.                                 return
  106.                             else:
  107.                                 self.multi_line.append (line)
  108.                                 line =    string.join (self.multi_line, '\n')
  109.                                 co = compile (line, repr(self), 'exec')
  110.                                 self.multi_line = []
  111.                         else:
  112.                             co = compile (line, repr(self), 'exec')
  113.                     except SyntaxError, why:
  114.                         if why[0] == 'unexpected EOF while parsing':
  115.                             self.push ('... ')
  116.                             self.multi_line.append (line)
  117.                             return
  118.                     exec co in self.local_env
  119.                     method = 'exec'
  120.             except:
  121.                 method = 'exception'
  122.                 self.multi_line = []
  123.                 (file, fun, line), t, v, tbinfo = asyncore.compact_traceback()
  124.                 self.log_info('%s %s %s' %(t, v, tbinfo), 'warning')
  125.                 traceback.print_exc()
  126.                 tbinfo = None
  127.         finally:
  128.             sys.stdout = oldout
  129.             sys.stderr = olderr
  130.         self.log_info('%s:%s (%s)> %s' % (
  131.             self.number,
  132.             self.line_counter,
  133.             method,
  134.             repr(line))
  135.             )
  136.         self.push_with_producer (p)
  137.         self.prompt()
  138.         
  139.     # for now, we ignore any telnet option stuff sent to
  140.     # us, and we process the backspace key ourselves.
  141.     # gee, it would be fun to write a full-blown line-editing
  142.     # environment, etc...
  143.     def clean_line (self, line):
  144.         chars = []
  145.         for ch in line:
  146.             oc = ord(ch)
  147.             if oc < 127:
  148.                 if oc in [8,177]:
  149.                     # backspace
  150.                     chars = chars[:-1]
  151.                 else:
  152.                     chars.append (ch)
  153.         return string.join (chars, '')
  154.  
  155. class monitor_server (asyncore.dispatcher):
  156.  
  157.     SERVER_IDENT = 'Monitor Server (V%s)' % VERSION
  158.  
  159.     channel_class = monitor_channel
  160.  
  161.     def __init__ (self, hostname='127.0.0.1', port=8023):
  162.         self.hostname = hostname
  163.         self.port = port
  164.         self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
  165.         self.set_reuse_addr()
  166.         self.bind ((hostname, port))
  167.         self.log_info('%s started on port %d' % (self.SERVER_IDENT, port))
  168.         self.listen (5)
  169.         self.closed        = 0
  170.         self.failed_auths = 0
  171.         self.total_sessions = counter()
  172.         self.closed_sessions = counter()
  173.  
  174.     def writable (self):
  175.         return 0
  176.  
  177.     def handle_accept (self):
  178.         conn, addr = self.accept()
  179.         self.log_info('Incoming monitor connection from %s:%d' % addr)
  180.         self.channel_class (self, conn, addr)
  181.         self.total_sessions.increment()
  182.  
  183.     def status (self):
  184.         return producers.simple_producer (
  185.             '<h2>%s</h2>'                        % self.SERVER_IDENT
  186.             + '<br><b>Total Sessions:</b> %s'        % self.total_sessions
  187.             + '<br><b>Current Sessions:</b> %d'    % (
  188.                 self.total_sessions.as_long()-self.closed_sessions.as_long()
  189.                 )
  190.             )
  191.  
  192. def hex_digest (s):
  193.     m = md5.md5()
  194.     m.update (s)
  195.     return string.joinfields (
  196.         map (lambda x: hex (ord (x))[2:], map (None, m.digest())),
  197.         '',
  198.         )
  199.  
  200. class secure_monitor_channel (monitor_channel):
  201.     authorized = 0
  202.     
  203.     def __init__ (self, server, sock, addr):
  204.         asynchat.async_chat.__init__ (self, sock)
  205.         self.server = server
  206.         self.addr = addr
  207.         self.set_terminator ('\r\n')
  208.         self.data = ''
  209.         # local bindings specific to this channel
  210.         self.local_env = {}
  211.         # send timestamp string
  212.         self.timestamp = str(time.time())
  213.         self.count = 0
  214.         self.line_counter = counter()
  215.         self.number = int(server.total_sessions.as_long())
  216.         self.multi_line = []
  217.         self.push (self.timestamp + '\r\n')
  218.  
  219.     def found_terminator (self):
  220.         if not self.authorized:
  221.             if hex_digest ('%s%s' % (self.timestamp, self.server.password)) != self.data:
  222.                 self.log_info ('%s: failed authorization' % self, 'warning')
  223.                 self.server.failed_auths = self.server.failed_auths + 1
  224.                 self.close()
  225.             else:
  226.                 self.authorized = 1
  227.                 self.push ('Python ' + sys.version + '\r\n')
  228.                 self.push (sys.copyright+'\r\n')
  229.                 self.push ('Welcome to %s\r\n' % self)
  230.                 self.prompt()
  231.                 self.data = ''
  232.         else:
  233.             monitor_channel.found_terminator (self)
  234.         
  235. class secure_encrypted_monitor_channel (secure_monitor_channel):
  236.     "Wrap send() and recv() with a stream cipher"
  237.  
  238.     def __init__ (self, server, conn, addr):
  239.         key = server.password
  240.         self.outgoing = server.cipher.new (key)
  241.         self.incoming = server.cipher.new (key)
  242.         secure_monitor_channel.__init__ (self, server, conn, addr)
  243.  
  244.     def send (self, data):
  245.         # send the encrypted data instead
  246.         ed = self.outgoing.encrypt (data)
  247.         return secure_monitor_channel.send (self, ed)
  248.  
  249.     def recv (self, block_size):
  250.         data = secure_monitor_channel.recv (self, block_size)
  251.         if data:
  252.             dd = self.incoming.decrypt (data)
  253.             return dd
  254.         else:
  255.             return data
  256.  
  257. class secure_monitor_server (monitor_server):
  258.     channel_class = secure_monitor_channel
  259.  
  260.     def __init__ (self, password, hostname='', port=8023):
  261.         monitor_server.__init__ (self, hostname, port)
  262.         self.password = password
  263.  
  264.     def status (self):
  265.         p = monitor_server.status (self)
  266.         # kludge
  267.         p.data = p.data + ('<br><b>Failed Authorizations:</b> %d' % self.failed_auths)
  268.         return p
  269.  
  270. # don't try to print from within any of the methods
  271. # of this object. 8^)
  272.  
  273. class output_producer:
  274.     def __init__ (self, channel, real_stderr):
  275.         self.channel = channel
  276.         self.data = ''
  277.         # use _this_ for debug output
  278.         self.stderr = real_stderr
  279.  
  280.     def check_data (self):
  281.         if len(self.data) > 1<<16:
  282.             # runaway output, close it.
  283.             self.channel.close()
  284.             
  285.     def write (self, data):
  286.         lines = string.splitfields (data, '\n')
  287.         data = string.join (lines, '\r\n')
  288.         self.data = self.data + data
  289.         self.check_data()
  290.         
  291.     def writeline (self, line):
  292.         self.data = self.data + line + '\r\n'
  293.         self.check_data()
  294.         
  295.     def writelines (self, lines):
  296.         self.data = self.data + string.joinfields (
  297.             lines,
  298.             '\r\n'
  299.             ) + '\r\n'
  300.         self.check_data()
  301.  
  302.     def ready (self):
  303.         return (len (self.data) > 0)
  304.  
  305.     def flush (self):
  306.         pass
  307.  
  308.     def softspace (self, *args):
  309.         pass
  310.  
  311.     def more (self):
  312.         if self.data:
  313.             result = self.data[:512]
  314.             self.data = self.data[512:]
  315.             return result
  316.         else:
  317.             return ''
  318.  
  319. if __name__ == '__main__':
  320.     import string
  321.     import sys
  322.     if '-s' in sys.argv:
  323.         sys.argv.remove ('-s')
  324.         print 'Enter password: ',
  325.         password = raw_input()
  326.     else:
  327.         password = None
  328.  
  329.     if '-e' in sys.argv:
  330.         sys.argv.remove ('-e')
  331.         encrypt = 1
  332.     else:
  333.         encrypt = 0
  334.  
  335.     print sys.argv
  336.     if len(sys.argv) > 1:
  337.         port = string.atoi (sys.argv[1])
  338.     else:
  339.         port = 8023
  340.  
  341.     if password is not None:
  342.         s = secure_monitor_server (password, '', port)
  343.         if encrypt:
  344.             s.channel_class = secure_encrypted_monitor_channel
  345.             import sapphire
  346.             s.cipher = sapphire
  347.     else:
  348.         s = monitor_server ('', port)
  349.     asyncore.loop()
  350.